﻿// -------------------------------------------------------------------------------------------------------
// LICENSE INFORMATION
//
// - This software is licensed under the MIT shared source license.
// - The "official" source code for this project is maintained at http://mtcagent.codeplex.com
//
// Copyright (c) 2010 OpenNETCF Consulting
//
// Permission is hereby granted, free of charge, to any person obtaining a copy of this software and 
// associated documentation files (the "Software"), to deal in the Software without restriction, 
// including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, 
// and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, 
// subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in all copies or substantial 
// portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT 
// NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. 
// IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, 
// WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE 
// SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 
// -------------------------------------------------------------------------------------------------------

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using OpenNETCF.Web;
using OpenNETCF.IoC;
using System.Xml.Linq;
using System.Diagnostics;
using System.IO;
using System.Net;
using System.Runtime.InteropServices;

namespace OpenNETCF.DeviceAdmin
{
    public class DeviceHandler : IHttpHandler
    {
        private enum TargetType
        {
            Directory,
            File
        }

        public string VirtualRoot = "/device";

        public bool IsReusable
        {
            get { return true; }
        }

        public void ProcessRequest(HttpContext context)
        {
            var et = Environment.TickCount;

            switch (context.Request.RequestType.ToUpper())
            {
                case "GET":
                    HandleGet(context);
                    break;
                case "POST":
                    HandlePost(context);
                    break;
                default:
                    throw new HttpException(HttpErrorCode.BadRequest, "Unsupported verb");
            }
            et = Environment.TickCount - et;

            Debug.WriteLineIf((et > 100), string.Format("DeviceHandler took {0}ms at {1}", et, context.Request.Path));
        }

        private string GetRequestData(HttpContext context)
        {
            var buffer = new byte[context.Request.InputStream.Length];
            context.Request.InputStream.Read(buffer, 0, buffer.Length);
            return Encoding.UTF8.GetString(buffer);
        }

        private string SanitizePath(string path)
        {
            return Uri.UnescapeDataString(path);
        }

        private void HandlePost(HttpContext context)
        {
            var entity = (GetRequestEntity(context) ?? string.Empty).Trim('/').ToLower();
            XDocument doc;
            string data;
            switch (entity)
            {
                case "time":
                    data = GetRequestData(context);
                    doc = XDocument.Parse(data);

                    var timeElement = doc.Element("SystemTime");
                    if (timeElement != null)
                    {
                        var time = DateTime.Parse(timeElement.Value);
                        LocalDevice.SystemTime = time;
                        break;
                    }

                    timeElement = doc.Element("LocalTime");
                    if (timeElement != null)
                    {
                        // we convert to UTC and set the system time (instead of the LocalTime) so that DST will not cause us to be off by an hour.
                        var time = DateTime.Parse(timeElement.Value).ToUniversalTime();
                        LocalDevice.LocalTime = time;
                        break;
                    }

                    throw new HttpException(HttpErrorCode.BadRequest, "Invalid Time data");

                case "name":
                    data = GetRequestData(context);
                    doc = XDocument.Parse(data);

                    var nameElement = doc.Element("HostName");
                    if (nameElement != null)
                    {
                        LocalDevice.HostName = nameElement.Value;
                        break;
                    }

                    throw new HttpException(HttpErrorCode.BadRequest, "Invalid Name data");
                default:
                    throw new HttpException(HttpErrorCode.BadRequest, "Invalid Entity URI");
            }
        }

        private void HandleGet(HttpContext context)
        {
            // determine path
            var entity = (GetRequestEntity(context) ?? string.Empty).Trim('/').ToLower();

            var doc = new XDocument(new XDeclaration("1.0", "utf-8", "yes"));

            switch (entity)
            {
                case "":
                    var rootNode = new XElement("Device");
                    AppendDeviceTimeElement(rootNode);
                    AppendNameElement(rootNode);
                    AppendOSElement(rootNode);
                    AppendNetworkElement(rootNode);

                    doc.Add(rootNode);
                    break;
                case "time":
                    AppendDeviceTimeElement(doc);
                    break;
                case "name":
                    AppendNameElement(doc);
                    break;
                case "os":
                    AppendOSElement(doc);
                    break;
                case "ipconfig":
                    AppendNetworkElement(doc);
                    break;
                default:
                    throw new HttpException(HttpErrorCode.BadRequest, "Invalid Entity URI");
            }

            context.Response.ContentType = "text/xml";
            context.Response.Write(doc.ToString(true));
        }

        private void AppendDeviceTimeElement(XContainer container)
        {
            var now = DateTime.Now;

            container.Add(
                new XElement("DeviceTime",
                    new XElement("SystemTime",
                        now.ToUniversalTime().ToString("s")),
                    new XElement("LocalTime",
                        now.ToString("s"))));
        }

        private void AppendNameElement(XContainer container)
        {
            var name = Dns.GetHostName();

            container.Add(
                new XElement("HostName", name));
        }

        private void AppendOSElement(XContainer container)
        {
            container.Add(
                new XElement("OS",
                    new XElement("Platform",
                        Environment.OSVersion.Platform.ToString()),
                    new XElement("Version",
                        Environment.OSVersion.Version.ToString(3))));
        }

        private void AppendNetworkElement(XContainer container)
        {
            container.Add(
                new XElement("Network",
                    new XElement("IP",
                        LocalDevice.CurrentIPAddress
                    )));
        }

        private string GetRequestEntity(HttpContext context)
        {
            var entity = context.Request.Url.Segments
                .SkipWhile(s => string.Compare(s.Trim('/'), "device", true) != 0) // find "device"
                .Skip(1) // skip "device"
                .FirstOrDefault(); // return the entity (or null)

            return entity;
        }
    }
}
